#include "Parser.h"

Parser::Parser(string dir) {

    this->docFile=NULL;
    this->cantFiles = 0;
    this->posTermino=0;
    this->blockSize=50;

    DIR *dp = opendir(dir.c_str());
    if(dp==NULL){
        MainWindow::getInstance()->writeToStdOuT("Directorio no valido para indexar");
        closedir(dp);
        return;
    }

    if (dir.substr(dir.length()-1,1).compare("/")!=0)
        dir.append("/");

    this->pathRaiz=dir;
    this->docFile = new ArchivoDocumentos("Docs.bin");
    this->docFile->setHeader(dir);
    this->parseFiles(dir);
    closedir(dp);
}

void Parser::parseFiles(string dir) {

    DIR *dp = opendir(dir.c_str());

    if (dir.substr(dir.length()-1,1).compare("/")!=0)
        dir.append("/");

    struct dirent *dirp;
    ArchivoAuxiliar *archivo=NULL;
    FileHandler fileHandler;
    unsigned int fileCount=0;

    while ((dirp = readdir(dp)) != NULL) {
        string path = dir;
        path.append(string(dirp->d_name));
        if (dirp->d_type == DT_DIR) {
            path.append("/");
            string directorioActual = string(dirp->d_name);
            if (directorioActual != "." && directorioActual != "..")
                parseFiles(path);
        } else {
            this->cantFiles += 1;
            this->posTermino=0;
            parseFile(path);
            fileCount+=1;

            if(fileCount==blockSize){
                groupTerms();
                archivo = fileHandler.merge(archivo, buffer);
                emptyBuffer();
                fileCount=0;
            }
        }
    }

    if(fileCount!=0){
        groupTerms();
        archivo = fileHandler.merge(archivo, buffer);
        emptyBuffer();
    }

    archivo->close();
    delete(dirp);
    delete (archivo);
    closedir(dp);
}

void Parser::parseFile(string path) {

    unsigned int pathMatchPos=0;
    while(pathMatchPos<path.length()&& pathMatchPos<pathRaiz.length() && ((string) path.substr(pathMatchPos,1)).compare(pathRaiz.substr(pathMatchPos,1))==0){
        pathMatchPos++;
    }

    string filePath = path.substr(pathMatchPos, path.length()-pathMatchPos);
    this->docFile->add(this->cantFiles, filePath);
    MainWindow::getInstance()->writeToStdOuT(filePath);

    string line;
    ifstream file(path.c_str());
    if (file.is_open()) {
        while (file.good()){
            getline(file, line);
            parseLine(line);
        }
        file.close();
    }
}

void Parser::addToken(string token) {

    this->posTermino+=1;
    RegistroAuxiliar newReg;
    newReg.setTermino(token);
    newReg.agregarDocumento(this->cantFiles);
    newReg.agregarPosicion(this->cantFiles,this->posTermino);
    buffer.push_back(newReg);
}


void Parser::groupTerms(){

    sort(buffer.begin(), buffer.end());
    vector<RegistroAuxiliar> newBuffer;
    vector<RegistroAuxiliar>::iterator it = buffer.begin();
    RegistroAuxiliar reg;
    RegistroAuxiliar regSig;
    unsigned int count=1;

    while (it < buffer.end()-1){

        vector<RegistroAuxiliar>::iterator aux = it+1;
        reg=*it;
        regSig=*aux;

        if(aux<buffer.end()-1){
            while(aux<buffer.end()-1 && reg.getTermino().compare(regSig.getTermino())==0){
                mergeReg(reg, regSig);
                count+=1;
                aux+=1;
                regSig=*aux;
            }
        }

        if(reg.getTermino().compare(regSig.getTermino())==0){
            count+=1;
            mergeReg(reg, regSig);
        }

        reg.setNroTermino(newBuffer.size()+1);
        newBuffer.push_back(reg);
        it+=count;
        count=1;
    };

    if(it == buffer.end()-1){
        reg=*it;
        reg.setNroTermino(newBuffer.size()+1);
        newBuffer.push_back(reg);
    }

    emptyBuffer();
    buffer=newBuffer;
}


void Parser::mergeReg(RegistroAuxiliar& reg, RegistroAuxiliar& regSig){

    vector<uint32_t>* documentos=regSig.getDocumentos();

    for(unsigned int i=0;i<documentos->size();i++){

        vector<uint32_t> p = regSig.getPosiciones(documentos->at(i));
        if(!reg.existDocument(regSig.getDocumentos()->at(i)))
            reg.agregarDocumento(documentos->at(i));

        for (unsigned int j = 0; j < p.size(); j++)
            reg.agregarPosicion(documentos->at(i), p.at(j));

        p.clear();
    }
    documentos->clear();
}


unsigned int Parser::getCantFiles(){
    return this->cantFiles;
}

void Parser::emptyBuffer() {
    buffer.clear();
}

Parser::~Parser() {
    emptyBuffer();
    if (docFile != NULL)
        delete (docFile);
}
